package org.jpropelleralt.set.impl;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.jpropelleralt.change.Change;
import org.jpropelleralt.set.SetChange;
import org.jpropelleralt.set.SetDelta;

/**
 * Immutable implementation of {@link SetChange}
 */
public class SetChangeDefault implements SetChange {

	boolean shallow;
	List<SetDelta> deltas;

	/**
	 * Make a new {@link SetChangeDefault}
	 * @param shallow	Whether the {@link Change} is shallow
	 * @param deltas	The deltas that have occurred to the {@link Set}
	 */
	private SetChangeDefault(boolean shallow, List<SetDelta> deltas) {
		super();
		this.shallow = shallow;
		this.deltas = deltas;
	}
	
	/**
	 * Make a new {@link SetChangeDefault}
	 * @param shallow	Whether the {@link Change} is shallow
	 * @param delta		The delta that has occurred to the {@link Set}
	 */
	private SetChangeDefault(boolean shallow, SetDelta delta) {
		this(shallow, makeSingleList(delta));
	}
	
	/**
	 * Create a shallow change with a single {@link SetDelta}
	 * @param delta		The {@link SetDelta}
	 * @return			The new change
	 */
	public static SetChangeDefault shallow(SetDelta delta) {
		return new SetChangeDefault(true, delta);
	}
	
	/**
	 * Create a deep change with a single {@link SetDelta} showing
	 * a complete alteration
	 * @param set		The {@link Set} that has changed
	 * @return			The new change
	 */
	public static SetChangeDefault deep(Set<?> set) {
		return new SetChangeDefault(false, SetDeltaDefault.newEntireSetAlteration(set));
	}
	
	private static List<SetDelta> makeSingleList(SetDelta delta) {
		List<SetDelta> list = new LinkedList<SetDelta>();
		list.add(delta);
		return Collections.unmodifiableList(list);
	}
	
	@Override
	public Change extend(Change existing) {
		
		if (!(existing instanceof SetChange)) {
			throw new IllegalArgumentException("A SetChange can only extend another SetChange");
		}
		
		SetChange setExisting = (SetChange)existing;
		
		//Extend different aspects of the change
		boolean extendsShallow = shallow() || existing.shallow();
		
		//Extend the delta list by adding our own deltas to the end
		LinkedList<SetDelta> extendsDeltas = new LinkedList<SetDelta>();
		for (SetDelta delta : setExisting) {
			extendsDeltas.add(delta);
		}
		for (SetDelta delta : this) {
			extendsDeltas.add(delta);
		}
		
		return new SetChangeDefault(extendsShallow, extendsDeltas);
	}


	@Override
	public Iterator<SetDelta> iterator() {
		return deltas.iterator();
	}
	
	@Override
	public boolean shallow() {
		return shallow;
	}
	
	@Override
	public String toString() {
		return (shallow ? "Shallow ":"Deep ") + "Set Change";
	}
	
}
